package pl.edu.agh.gt.stock;

import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

import pl.edu.agh.gt.data.DataProvider;
import pl.edu.agh.gt.data.Rate;
import pl.edu.agh.gt.data.RatesHistory;
import pl.edu.agh.gt.gui.ChartDisplay;
import pl.edu.agh.gt.strategy.BuyAndHold;
import pl.edu.agh.gt.strategy.GreedyStrategy;
import pl.edu.agh.gt.strategy.Strategy;

/**
 * BASE = USD QUOTE = EUR
 * 
 * private/protected
 * 
 * asercje
 * 
 * @author kpajak
 * 
 */

public class Stock {

	private final static Logger log = Logger.getLogger(Stock.class);

	private RatesHistory ratesHistory;
	private Broker broker;
	private Broker counterBroker;

	private Map<String, Trader> traders;

	private Rate currRate;
	private int currIter;

	private List<StockListener> listeners;

	private Stock() {
		broker = new BrokerImpl();
		counterBroker = new CounterBrokerImpl();
		traders = new HashMap<String, Trader>();
		listeners = new LinkedList<StockListener>();
	}

	public Stock(String path, Date start, Date end) throws IOException {
		this();
		ratesHistory = DataProvider.readData(new File(path)).getPeriod(start, end);
	}

	public Stock(RatesHistory rates) {
		this();
		ratesHistory = rates;
	}

	public void runStock() throws Exception {

		List<Rate> rates = ratesHistory.asSortedList();
		currRate = rates.get(0);

		for (currIter = 0; currIter < rates.size(); currIter++) {

			float diff = rates.get(currIter).getClose() / currRate.getClose() - 1.0f;
			currRate = rates.get(currIter);

			log.debug(" --------- ["+currIter+"]\tTime " + currRate.getTime() + " ---  rate = " + currRate.getClose()
					+ " ------ \t DIFF = " + diff);

			announceOpen(currIter, currRate);

			for (Trader t : traders.values()) {
				t.trade(currIter);
			}

			announceClose(currIter, currRate);
		}

		// force traders to close positions at the end
		for (Trader t : traders.values())
			t.close(currRate);

	}

	private void announceOpen(int iter, Rate r) throws Exception {
		for (StockListener l : listeners)
			l.onStockOpen(this, iter, r);
	}

	private void announceClose(int iter, Rate r) throws Exception {
		for (StockListener l : listeners)
			l.onStockClose(this, iter, r);
	}

	public void addTrader(Trader t) {
		traders.put(t.name, t);
	}

	public Trader getTrader(String name) {
		return traders.get(name);
	}

	public void removeTraders() {
		// force traders to close positions
		for (Trader t : traders.values())
			t.close(currRate);
		traders.clear();
	}

	public Trader removeTrader(Trader t) {
		// force trader to close position
		if (t.quoteCurrency > 0)
			t.close(currRate);
		traders.remove(t.name);
		return t;
	}

	public Broker getBroker() {
		return broker;
	}

	public RatesHistory getRatesHistory() {
		return ratesHistory;
	}

	public void addListener(StockListener l) {
		listeners.add(l);
	}

	public void removeListener(StockListener l) {
		listeners.remove(l);
	}

	public Broker getCounterBroker() {
		return counterBroker;
	}
	
	public int getCurrentIteration() {
		return currIter;
	}
	
	/**
	 * Simulation with default params
	 */
	public static void main(String[] args) {

		try {
			Logger.getLogger("pl.edu.agh.gt.strategy").setLevel(Level.DEBUG);
			Logger.getLogger("pl.edu.agh.gt.stock").setLevel(Level.DEBUG);

			@SuppressWarnings("deprecation")
			Date start = new Date(2007 - 1900, 1, 1);

			@SuppressWarnings("deprecation")
			Date end = new Date(2007 - 1900, 1, 29);

			// Stock stock = new
			// Stock("C:\\Users\\kpajak\\Documents\\Studia\\MAGISTERA\\materialy_mgr\\EURUSD_hour.csv",start,end);
			Stock stock = new Stock("src/main/resources/EURUSD_hour.csv", start, end);

			createTraders(stock);

			stock.runStock();

			for (Trader t : stock.traders.values())
				log.info("Trader " + t.name + " total:" + t.evaluate(stock.ratesHistory.getLastRate())
						+ ", maxInvested: " + t.maxInvested + ", buys: " + t.buyActions + ", close: " + t.closeActions
						+ ", sell: " + t.sellActions);

			ChartDisplay.tradingChart("Trader performance compared to Buy&Hold", stock.ratesHistory,
					stock.getTrader("GREEDY").getTradingHistory(), stock.getTrader("BUY&HOLD").getTradingHistory());

		} catch (Exception e) {
			log.error("Stock exception", e);
		}

	}

	static void createTraders(Stock stock) {
		// lossTolerancy=10.971557 greedA=96.206276 desiredEarn=60.558296
		// enteringValue=3965.797 maxLoss=-94.22526

		// lossTolerancy=0.5157306 greedA=98.449036 desiredEarn=11.324305
		// enteringValue=3341.078 maxLoss=-97.571556

		// lossTolerancy=4.0945487 greedA=1182.5867 desiredEarn=121.56686
		// enteringValue=1930.7538 maxLoss=-94.29898 - malejacy - WOW

		// lossTolerancy=1.3363646 greedA=855.69104 desiredEarn=9.304913
		// enteringValue=640.66534 maxLoss=-147.75618

		// 2010
		// lossTolerancy=3.2002985 greedA=66.82258 desiredEarn=1856.418
		// enteringValue=626.36926 maxLoss=-33.88853

		// lossTolerancy=2.268162 greedA=223.79306 desiredEarn=75.60528
		// enteringValue=1143.9745 maxLoss=-55.089188
		
		// 1000x100 2005
		//FITNESS=1395.4307, gens: lossTolerancy=1.619824 greedA=35.276775 desiredEarn=163.81725 enteringValue=352.23862 maxLoss=-99.91851
		//FITNESS=1384.9932, gens: lossTolerancy=0.6590527 greedA=99.87568 desiredEarn=68.156136 enteringValue=214.89412 maxLoss=-114.60545
		//FITNESS=1414.3877, gens: lossTolerancy=0.6590527 greedA=33.73168 desiredEarn=168.07773 enteringValue=162.93074 maxLoss=-127.61178
		//FITNESS=1457.9268, gens: lossTolerancy=1.0503703 greedA=48.280487 desiredEarn=164.90982 enteringValue=226.37971 maxLoss=-368.31598
		
		// ograniczene tradu do 24h
		//lossTolerancy=2.1077633 greedA=263.8331 desiredEarn=303.516 enteringValue=1723.1388 maxLoss=-3.5311584

		Strategy strategy = new GreedyStrategy(2.1077633 ,263.8331 ,303.516 ,1723.1388 ,-3.5311584);
		stock.addTrader(new Trader("GREEDY", stock, strategy));

		strategy = new BuyAndHold();
		stock.addTrader(new Trader("BUY&HOLD", stock, strategy));

		// enable trade logging
		for (Trader t : stock.traders.values())
			t.setLogTrading(true);
		
		
	}

	
}
